
<html><HEAD>
<LINK REL=STYLESHEET HREF="default.css" TYPE="text/css">
<TITLE>
Accessing PowerBuilder COM servers from clients</TITLE>
</HEAD>
<BODY>

<!-- Header -->
<p class="ancestor" align="right"><A HREF="apptechp173.htm">Previous</A>&nbsp;&nbsp;<A HREF="apptechp175.htm" >Next</A>
<!-- End Header -->
<A NAME="CHDDBCEE"></A><h1>Accessing PowerBuilder COM servers from clients</h1>
<A NAME="TI5403"></A><p>You can access the methods on a PowerBuilder COM component
from clients built with any COM-compliant tool. For COM+,
the client must have Microsoft Windows Installer. The COM server
must be registered on the client machine, or the COM+ application
proxy file must be installed. </p>
<A NAME="TI5404"></A><p>For how to access PB COM servers from a remote client, see <A HREF="apptechp174.htm#BABCAFDC">"Using PowerBuilder COM servers
and objects with DCOM"</A>.</p>
<A NAME="TI5405"></A><p>The following examples show how you access a PowerBuilder
COM object from Visual Basic or C++. They use
a PowerBuilder COM object that was generated from a user object
called <b>ccuo_employee</b> and has the Program
ID <b>PB115.employee</b>.</p>
<A NAME="TI5406"></A><p>For information about building PowerBuilder clients and an
example using the same COM object, see <A HREF="apptechp175.htm#CHDBJEFH">Chapter 27, "Building a COM or COM+ Client."</A></p>
<A NAME="TI5407"></A><h2>Visual Basic as client</h2>
<A NAME="TI5408"></A><p>In Visual Basic, you can connect to the registered object
using its program ID (late binding). In Visual Basic 5 or later,
you can also use its class name (early binding). </p>
<A NAME="TI5409"></A><p><img src="images/proc.gif" width=17 height=17 border=0 align="bottom" alt="Steps"> To access a PowerBuilder COM object in Visual
Basic:</p>
<ol><li class=fi><p>Do one of the following:</p><p><A NAME="TI5410"></A>
<ul>
<li class=fi>Declare an object and connect
to it using its program ID: <br>
<p><PRE> Dim EmpObj As Object<br>Set EmpObj = CreateObject("PB115.employee")</PRE><br></li>
<li class=ds>Add a reference to the generated type library for
the PowerBuilder COM object to your project, then declare an instance
of the object using its class name (in Visual Basic 5 or later):<p><PRE> Dim EmpObj As New CoEmployee</PRE>
</li>
</ul>

                      </p></li>
<li class=ds><p>Check that the connection was established: </p><p><p><PRE> Dim response<br>If EmpObj Is Nothing Then<br>response = MsgBox("Creating Employee Object", <br>      vbOKOnly, "Error")<br>End If</PRE></p></li>
<li class=ds><p>Access functions or properties of the object:</p><p><p><PRE> Dim units, time as Long<br>Dim DoubleReturn as Double<br>Dim StringReturn As String<br> <br>DoubleReturn = EmpObj.f_calcdayavg units, time <br>StringReturn = EmpObj.f_teststring<br>EmpObj.ll_hours = 37</PRE></p></li>
<li class=ds><p>Destroy the object:</p><p><p><PRE> Set EmpObj = Nothing</PRE></p></li></ol>
<br><A NAME="TI5411"></A><h2>C++ as client</h2>
<A NAME="TI5412"></A><p>In C++, you use COM library functions to
create an instance of a PowerBuilder COM object. You also need to
use C/C++ definitions of the PowerBuilder COM
objects when you build the client. The Microsoft IDL (MIDL) compiler generates
these definitions from the IDL file created by the PowerBuilder COM
generator. </p>
<A NAME="TI5413"></A><p>For example, using the IDL file generated for the Employee
PowerBuilder COM object:<p><PRE> midl.exe employee.idl</PRE></p>
<A NAME="TI5414"></A><p>The MIDL compiler generates a header file (<i>employee.h</i>)
containing definitions of the interfaces of all the objects in the
IDL file and a C file (<i>employee_i.c</i>)
that defines the CLSIDs and Interface IDs (IIDs) associated with
the object.</p>
<p><img src="images/note.gif" width=17 height=17 border=0 align="bottom" alt="Note"> <span class=shaded>Additional files </span> <A NAME="TI5415"></A>The MIDL compiler also generates proxy/stub code
(in <i>employee_p.c</i> and <i>dlldata.c</i>),
but you do not need to use the proxy/stub code to build
the C++ client executable or access the PowerBuilder
COM object. </p>
<p><b>Building a client</b>   To build a C++ client executable that can
access methods in a PowerBuilder COM object, you create a C++ source
file that includes the generated header file, compile both the C++ client
source file and the C file generated by the MIDL compiler, then
link the resulting object files to create the executable. </p>
<A NAME="TI5416"></A><p>For the Employee example:<A NAME="TI5417"></A>
<ol>
</li>
<li class=ds>Create
a C++ source file called <i>client.cpp</i> (shown
below).</li>
<li class=ds>Compile <i>client.cpp</i>.</li>
<li class=ds>Compile <i>employee_i.c</i>.</li>
<li class=ds>Link <i>client.obj</i> and <i>employee_i.obj</i> to
create an executable&#8212;for example, <i>employee_ecl.exe</i>.
</li>
</ol>
</p>
<p><b>Employee.h </b>   The following code fragments from the <i>employee.h</i> header
file generated by the MIDL compiler show the definitions to be used
by C++ clients:</p>
<A NAME="TI5418"></A><p><p><PRE> typedef interface DIEmployee DIEmployee;<br>EXTERN_C const IID IID_DIEmployee;<br> <br>interface DECLSPEC_UUID("A2F59F71-D5FB-11D1-92B9-00A0247712F1")<br>    DIEmployee : public IDispatch<br>    {<br>    public: <br>    virtual /* [id] */ HRESULT STDMETHODCALLTYPE<br>       f_calcdayavg( <br>      /* [in] */ long units,<br>      /* [in] */ long time,<br>      /* [retval][out] */ double __RPC_FAR <br>        *retval) = 0;<br> <br>    virtual /* [id] */ HRESULT <br>        STDMETHODCALLTYPE f_teststring( <br>        /* [retval][out] */ BSTR __RPC_FAR<br>           *retval) = 0;<br>    };<br> <br>EXTERN_C const CLSID CLSID_CoEmployee;</PRE></p>
<p><b>Client.cpp </b>   The following sample client file uses the MIDL-generated C/C++ definitions
of PowerBuilder COM objects. For further information on the COM
API calls shown in <i>client.cpp</i>, see the Microsoft
Software Development Kit documentation.</p>
<A NAME="TI5419"></A><p><p><PRE> #include &lt;windows.h&gt;<br>// employee.h I( from MIDL.EXE)<br>#include "employee.h"<br> <br>int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)<br>{<br>    HRESULT hr;<br>    DIEmployee      *pDIEmployee = 0;<br> <br>    //  Initialize COM<br>    CoInitialize(0);<br> <br>    hr = CoCreateInstance(CLSID_CoEmployee,NULL,<br>       CLSCTX_INPROC_SERVER, IID_DIEmployee,<br>      (void **)&amp;pDIEmployee);<br>    if (FAILED(hr))<br>    ErrorMessage("CoCreateInstance", hr);<br> <br>    // variables for methods<br>    long units, time;<br>    double dReturn;<br>    BSTR  strReturn = NULL;<br>    <br>    // call methods<br>    hr = pDIEmployee-&gt;f_calcdayavg(units,<br>                   time,&amp;dReturn);<br>    if (FAILED(hr))<br>    ErrorMessage("f_calcdayavg",hr);  <br> <br>    hr = pDIEmployee-&gt;f_teststring(&amp;strReturn);<br>    if (FAILED(hr))<br>    ErrorMessage("f_teststring",hr);<br> <br>    // release the interface ptr<br>    pDIEmployee-&gt;Release();<br> <br>    CoFreeUnusedLibraries();<br>    //  all done!<br>    <br>CoUninitialize();<br>    return 0;<br>}</PRE></p>
<A NAME="BABCAFDC"></A><h2>Using PowerBuilder COM servers and objects with DCOM</h2>
<A NAME="TI5420"></A><p>A PowerBuilder COM object can be activated from remote clients
using DCOM. The object must be activated in a server process on
the designated host computer. Out-of-process servers (EXE files)
create a server process, but in-process servers (DLL files)
must be hosted in a surrogate process.</p>
<A NAME="TI5421"></A><p>COM provides a general-purpose surrogate host (<i>DLLHOST.EXE</i>)
that can be used to host PowerBuilder COM server DLLs. Marking PowerBuilder
COM servers to use a surrogate host is the primary step in enabling
remote client access to your PowerBuilder COM objects. You can use
the DCOM configuration utility (<i>DCOMCNFG.EXE</i>)
to change values for location, security, and identity, but in most
cases the defaults are adequate. For more information, see the online
Help for the <b>DCOMCNFG</b> utility.</p>
<A NAME="TI5422"></A><h4>Enabling PowerBuilder COM servers to use a surrogate
host</h4>
<A NAME="TI5423"></A><p>There are two ways to enable PowerBuilder COM servers to use
a surrogate:<A NAME="TI5424"></A>
<ul>
<li class=fi>Use the registry editor (<i>REGEDIT32.EXE</i>)
to edit the PowerBuilder COM server's AppID registry entry. </li>
<li class=ds>Use the OLE/COM Object Viewer (<i>OLEVIEW.EXE</i>)
provided with Microsoft Visual C++ 5.0 or greater. 
</li>
</ul>
</p>
<A NAME="TI5425"></A><p>Using OLEVIEW is the preferred approach, because manually
editing your computer's registry may render all or parts
of your computer's configuration inoperable.</p>
<A NAME="TI5426"></A><p><img src="images/proc.gif" width=17 height=17 border=0 align="bottom" alt="Steps"> To enable a COM server to use a surrogate process
using the registry editor:</p>
<ol><li class=fi><p>Open the project used to generate the server
and copy the PowerBuilder COM server's AppID value from
the General property page.</p></li>
<li class=ds><p>Run <i>REGEDIT.EXE</i>, find the
server's AppID key in<i> My Computer\HKEY_CLASSES_ROOT\AppID</i>,
and select it.</p></li>
<li class=ds><p>Select Edit&gt;New&gt;String Value
from the menu bar.</p></li>
<li class=ds><p>Enter the name <FONT FACE="Courier New">DllSurrogate</FONT> and
leave the data field empty.</p><p>An empty data field tells COM to use the default surrogate
host (<i>DLLHOST.EXE</i>). The AppID value keys should
look like this:</p><A NAME="TI5427"></A><table cellspacing=0 cellpadding=6 border=1 frame="void" rules="all"><tr><th  rowspan="1"  ><A NAME="TI5428"></A>Name</th>
<th  rowspan="1"  ><A NAME="TI5429"></A>Data</th>
</tr>
<tr><td  rowspan="1"  ><A NAME="TI5430"></A>(Default)</td>
<td  rowspan="1"  ><A NAME="TI5431"></A>PowerBuilder 11.5 generated server: <i>servername</i>.dll</td>
</tr>
<tr><td  rowspan="1"  ><A NAME="TI5432"></A>DllSurrogate</td>
<td  rowspan="1"  ><A NAME="TI5433"></A>""</td>
</tr>
</table>
</li></ol>
<br><A NAME="TI5434"></A><p><img src="images/proc.gif" width=17 height=17 border=0 align="bottom" alt="Steps"> To enable a COM server to use a surrogate process
using the OLE/COM Object Viewer:</p>
<ol><li class=fi><p>Run <i>OLEVIEW.EXE</i>.</p></li>
<li class=ds><p>Expand the Automation Objects in the list view
and select an object in your PowerBuilder COM server.</p></li>
<li class=ds><p>Select an object associated with your PowerBuilder
COM server. </p></li>
<li class=ds><p>Select the Implementation tab.</p></li>
<li class=ds><p>Select the Inproc Server tab and check the Use
Surrogate Process check box.</p></li></ol>
<br><A NAME="TI5435"></A><h4>Configuring client computers to activate remote
PowerBuilder COM objects</h4>
<A NAME="TI5436"></A><p>To activate a remote component, a client application must
pass the object's class identifier (CLSID) in the activation
request to the remote host. </p>
<A NAME="TI5437"></A><p>Some clients, such as those built with PowerBuilder or C++,
can use the object's CLSID in the method call when they
create an instance of a remote object. These client applications
do not require any client-side configuration. </p>
<A NAME="TI5438"></A><p>Most clients reference an object by its name (ProgID) rather
than its CLSID. In these cases the client computer's registry
must contain the information necessary to map a ProgID to a CLSID
in order to make the remote activation request. You can use either
of two methods to add the required registry information to the client
computer:<A NAME="TI5439"></A>
<ul>
<li class=fi>Register the PowerBuilder COM server
on each client computer that requires remote access.<br>
To use this method, you must be able to locate the appropriate
version of <i>PBVMn0.DLL</i>.<br></li>
<li class=ds>On the host where the PowerBuilder COM server is
registered, export the required registry information into .REG files
using <i>REGEDIT.EXE</i>, then copy and import these
files into the registry of each client computer that requires remote
access.<br>
For each PowerBuilder COM object, export the following registry
keys:<p><PRE> HKEY_CLASSES_ROOT\PB115.<i>objectname</i><br>HKEY_CLASSES_ROOT\PB115.<i>objectname</i>.<i>version_number</i><br>HKEY_CLASSES_ROOT\CLSID\{<i>objects_clsid</i>}</PRE><br><br>
You may also need the following registry keys for the PowerBuilder
COM server:<p><PRE> HKEY_CLASSES_ROOT\TypeLib\{<i>comserver_typelib_id</i>}<br>HKEY_CLASSES_ROOT\AppID\{<i>comserver_application_id</i>}</PRE><br>
</li>
</ul>
</p>
<A NAME="CHDDHDHJ"></A><h4>Connecting to remote objects
using PowerBuilder</h4>
<A NAME="TI5440"></A><p>PowerBuilder clients can use the <b>ConnectToNewRemoteObject</b> function
to activate remote objects, as shown in this code fragment:<p><PRE> OLEObject remobj<br>remobj = CREATE OLEObject<br>remobj.ConnectToNewRemoteObject("myremotehostname",  &amp;<br>    "PB115.employee")</PRE></p>
<A NAME="TI5441"></A><p>You can also use the remote object's CLSID string
in the classname parameter:<p><PRE> remobj.ConnectToNewRemoteObject("myremotehostname",  &amp;<br>    "clsid:0EA53FED-646A-11D2-BF8E-00C04F795006")</PRE></p>
<A NAME="TI5442"></A><p>The use of the object's CLSID as the classname parameter
eliminates the need for any client-side configuration.</p>
<A NAME="TI5443"></A><h4>Connecting to remote objects using C++</h4>
<A NAME="TI5444"></A><p>C++ clients that use header files created
from the generated PowerBuilder COM server IDL file can use the
remote object's CLSID in the activation request: <p><PRE> COSERVERINFO ServerInfo;<br>MULTI_QI    mqi[1];<br>OLECHAR    wszHostName[MAXFILE];<br>LPTSTR     pszHost=NULL;<br> <br>memset(&amp;ServerInfo,0,sizeof(ServerInfo));<br>pszHost =GetUserRequestedHostName();<br>mbstowcs(wszHostName,pszHost,MAXFILE);<br> <br>ServerInfo.pwszName = wszHostName;<br>mqi[0].pIID = &amp;IID_Iemployee;<br>mqi[0].pItf = NULL;<br>mqi[0].hr   = S_OK;<br> <br>// Create employee object on the desired server<br>hr = CoCreateInstanceEx(CLSID_Employee,NULL,<br>       CLSCTX_REMOTE_SERVER,&amp;ServerInfo,1,mqi);</PRE></p>

